# jcrust for Py4IJ - Interactive jython shell
# Copyright (c) 2008 Ricardo Henriques <paxcalpt at gmail.com>
#
# based on the previous work of:
# jcrust  - Interactive jython shell
# Copyright (c) 2003 Ferdinand Jamitzky <ferdinand at jamitzky.de>
#
# based on:
# JinSitu - Interactive introspection environment for Java and Jython.
# Copyright (c) 2001 Michael Krause <michael@krause-software.de>
#
# Terms and Conditions
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import os, thread, imp, traceback
import sys
import types
import pprint
from UserDict import UserDict
import traceback, tempfile

import java, javax
from java.util import Vector
from pawt import swing
from java.awt import *
from java.awt import Font
from java.awt.datatransfer import *
from javax.swing.KeyStroke import getKeyStroke
from javax.swing.tree import DefaultMutableTreeNode
from java.awt.event import KeyEvent

#from org.python.core import PyStringMap, PyJavaPackage, PyJavaInstance, PySystemState
from org.python.core import PyStringMap, PyJavaPackage, PySystemState
from org.python.core import PyInstance as PyJavaInstance
#from org.python.core import PyStringMap, PyJavaPackage, PySystemState
#from org.python.core import PyJavaType as PyJavaInstance 

import JScrapbook
import console

header = """\
Jython %(version)s on %(platform)s
%(copyright)s
""" % {'version':sys.version, 'platform':sys.platform, 'copyright':sys.copyright}

objecttype2icon = [
     [ 'java_class', 'JavaClass_.gif' ],
     [ 'interface', 'JavaInterface_.gif' ],

     [ 'java_field', 'FieldPublicStatic_.gif' ],
     [ 'field_public', 'FieldPublic_.gif' ],
     [ 'field_protected', 'FieldProtected_.gif' ],
     [ 'field_package', 'FieldPackage_.gif' ],
     [ 'field_private', 'FieldPrivate_.gif' ],
     [ 'field_public_static', 'FieldPublicStatic_.gif' ],
     [ 'field_protected_static', 'FieldProtectedStatic_.gif' ],
     [ 'field_package_static', 'FieldPackageStatic_.gif' ],
     [ 'field_private_static', 'FieldPrivateStatic_.gif' ],

     [ 'constructor_public', 'MethodPublic_.gif' ],
     [ 'constructor_protected', 'MethodProtected_.gif' ],
     [ 'constructor_package', 'MethodPackage_.gif' ],
     [ 'constructor_private', 'MethodPrivate_.gif' ],

     [ 'java_constructor', 'MethodPublicStatic_.gif' ],
     [ 'java_method', 'MethodPublicStatic_.gif' ],
     [ 'method_public', 'MethodPublic_.gif' ],
     [ 'method_protected', 'MethodProtected_.gif' ],
     [ 'method_package', 'MethodPackage_.gif' ],
     [ 'method_private', 'MethodPrivate_.gif' ],
     [ 'method_public_static', 'MethodPublicStatic_.gif' ],
     [ 'method_protected_static', 'MethodProtectedStatic_.gif' ],
     [ 'method_package_static', 'MethodPackageStatic_.gif' ],
     [ 'method_private_static', 'MethodPrivateStatic_.gif' ],
     [ 'python_method', 'PySnake.gif' ],

     [ 'module', 'Zip_.gif' ],
     [ 'java_package', 'JavaPackage_.gif' ],
     [ 'callable', 'MethodPublic_.gif' ],
     [ 'none', 'DebugRestart_.gif' ],
     [ 'string', 'Font_.gif' ],
     [ 'file', 'TextFile_.gif' ],
     [ 'instance', 'Class_.gif' ],
     [ 'java_instance', 'JavaRuntime_.gif' ],
     [ 'default', 'Directory.gif' ]
     ]

objecttype2icon_dict = {}
objecttype_rank = {}
rank = 0
for i in objecttype2icon:
    objecttype2icon_dict[i[0]] = i[1]
    objecttype_rank[i[0]] = rank
    rank += 1

def getObjectType(dobject):
    object = dobject.icontype or dobject.cls or dobject.value
    try:
        if java.lang.Class.isInterface(object):
            return 'interface'
    except: pass
    if is_module(object): return 'module'
    if isinstance(object, types.MethodType) \
       or isinstance(object, types.FunctionType): return 'python_method'
    if callable(object): return 'callable'
    if isinstance(object, types.NoneType): return 'none'
    if isinstance(object, types.StringType): return 'string'
    if isinstance(object, types.FileType): return 'file'
    if type(object) == types.InstanceType: return 'instance'
    if type(object) is not PyJavaInstance:
        if type(object) is PyJavaPackage: return 'java_package'
        if type(object) is types.TypeType: return 'java_class'
        if type(object) is java.lang.reflect.Constructor: return 'java_constructor'
        if type(object) is java.lang.reflect.Method: return 'java_method'
        if type(object) is java.lang.reflect.Field: return 'java_field'
        return 'default'

    object_type = object.__class__
    if object_type is java.lang.reflect.Method: mtext = 'method'
    elif object_type is java.lang.reflect.Constructor: mtext = 'constructor'
    elif object_type is java.lang.reflect.Field: mtext = 'field'
    else: return 'java_instance'

    m = object.getModifiers()
    if m & java.lang.reflect.Modifier.PRIVATE: mtext += '_private'
    elif m & java.lang.reflect.Modifier.PROTECTED: mtext += '_protected'
    elif m & java.lang.reflect.Modifier.PUBLIC: mtext += '_public'
    else: mtext += '_package'
    if m & java.lang.reflect.Modifier.STATIC: mtext += '_static'

    return mtext

def object_sort(items):
    items.sort(object_sort_cmp)

def object_sort_cmp(a, b):
    otype_a, otype_b = getObjectType(a[1]), getObjectType(b[1])
    rank_a, rank_b = objecttype_rank[otype_a], objecttype_rank[otype_b]
    # first sort by type
    if rank_a < rank_b: return -1
    if rank_a > rank_b: return 1
    # then sort by name
    if a[0] < b[0]: return -1
    if a[0] > b[0]: return 1
    return 0

def docSignature(jr_object):
    """Return signature for a given java reflection object (constructor, method, field)"""
    if isinstance(jr_object, java.lang.reflect.Field):
        return jr_object.getName()

    args = args2docstr(jr_object.getParameterTypes())
    name = jr_object.getName()
    if isinstance(jr_object, java.lang.reflect.Constructor):
        # strip package from class name
        try:
            package_prefix = jr_object.getDeclaringClass().getPackage().getName() + '.'
            name = name[len(package_prefix):]
        except: pass
    sig = name + '(' + args + ')'
    return sig

def args2docstr(jargs):
    """Returns the argument list as string matching the
       anchors of the javadoc HTML pages.
    """
    m_args = ', '.join(args2str(jargs))
    return m_args

def args2str(jargs):
    return map(argName, jargs)

def argName(cls):
    if type(cls) is types.ClassType:
        return cls.__name__
    elif java.lang.Class.isArray(cls):
        return argName(cls.getComponentType()) + '[]'
    else:
        name = java.lang.Class.getName(cls)
        name = name.replace('$', '.') # handle inner class names
        return name

def is_documented_type(object):
    return isinstance(object, java.lang.reflect.Method) \
        or isinstance(object, java.lang.reflect.Constructor) \
        or isinstance(object, java.lang.reflect.Field)

def is_primitive(object):
    return type(object) in [types.NoneType, types.FloatType, types.IntType, types.StringType]

def is_module(object):
    return type(object) is types.ModuleType \
           or type(object) is PySystemState

def getJavaAttributes(object):
    dict = getInterfaces(object)
    dict.update(getMethods(object))
    dict.update(getConstructors(object))
    dict.update(getFields(object))
    return dict

def getInterfaces(object):
    return getAttributes(object, java.lang.Class.getInterfaces)

def getConstructors(object):
    return getAttributes(object, java.lang.Class.getDeclaredConstructors)

def getMethods(object):
    return getAttributes(object, java.lang.Class.getDeclaredMethods,
                         java.lang.reflect.Method.getDeclaringClass)

def getFields(object):
    return getAttributes(object, java.lang.Class.getDeclaredFields)

def getAttributes(object, func, filter_func=None):
    dict = {}
    try:
        for a in func(object):
            if not filter_func or filter_func(a) is object:
                key = docSignature(a)
                dict[key] = DObject(key, a)
    except: pass
    return dict

def get_dict_attr(object):
    dict = {}
    try:
        for k, v in object.__dict__.items():
            dict[k] = DObject(k, v)
    except: pass
    return dict

def get_dir_attrs(object):
    dict = {}
    try:
        for key in dir(object):
            try:
                dict[key] = DObject(key, getattr(object, key))
            except: pass
    except: pass
    return dict

class DObject:
    def __init__(self, key, value, cls=None, icontype=None, accessible=1):
        self.key = key
        self.value = value
        self.cls = cls
        self.icontype = icontype
        self.accessible = accessible

    def __str__(self):
        return str(self.key)

    def __repr__(self):
        return "[%s, %s, %s, %s, %d]" % ( self.key, str(self.value), str(self.cls),
                                      str(self.icontype), str(self.accessible) )

    def __eq__(self, other):
        return self.key == other.key \
               and self.value == other.value \
               and self.cls == other.cls \
               and self.icontype == other.icontype \
               and self.accessible == other.accessible

    def __ne__(self, other):
        return not self.__eq__(other)

class ObjectNode(DefaultMutableTreeNode):

    def __init__(self, *args, **kwargs):
        DefaultMutableTreeNode.__init__(self, *args, **kwargs)
        self.expanded = 0
        self.m_children = []
        self.children = Vector()
        self.model = ObjectNodeModel()
        self.nodes = {}
        self.is_leaf = -1

    def toString(self):
        """The result from toString() is automatically displayed as node label"""
        return str(self.userObject)

    def _initChildren(self):
        if not self.m_children:
            self.m_children = self.model.getChildren(self.userObject)
            items = self.m_children.items()
            object_sort(items)
            self.m_children_keys = map(lambda x: x[0], items)

    def getChildCount(self):
        if self.userObject is None:
            return

        self._initChildren()
        return len(self.m_children)

    def isLeaf(self):
        if self.m_children:
            return len(self.m_children) == 0
        if self.is_leaf == -1:
            try:
                children = self.model.getChildren(self.userObject, CheckingDict())
                self.is_leaf = len(children) == 0
            except DictWriteException:
                self.is_leaf = 0
        return self.is_leaf

    def getChildAt(self, i):
        self._initChildren()

        if not self.nodes.has_key(i):
            self.expanded = 1
            key = self.m_children_keys[i]
            value = self.m_children[key]
            node = ObjectNode(value)
            node.setParent(self)
            self.nodes[i] = node
        return self.nodes[i]

    def getIndex(self, aChild):
        return self.children.indexOf(aChild)

class DictWriteException(Exception):
    pass

class CheckingDict(UserDict):
    def __setitem__(self, key, value):
        raise DictWriteException

    def update(self, dict):
        if dict:
            raise DictWriteException

class ObjectNodeModel:

    def getChildren(self, dobject, mydict={}):
        """Return a dictionary with the attributes or contents of object."""
        object = dobject.value

        if is_primitive(object):
            return {}

        if isinstance(object, java.lang.reflect.Method):
            return {}

        dict = mydict.copy()

        if dobject.cls:
            dict.update(self.getInstanceChildren(dobject))
            return dict

        objtype = type(object)
        dict.update(self.getRefMethods(object))

        if objtype in (types.DictType, PyStringMap):
            for k, v in object.items():
                dict[k] = DObject(k, v)
        dict.update(getJavaAttributes(object))
        if objtype is types.InstanceType:
            dict.update(self.getPyInstanceChildren(object))
        else:
            dict.update(self.getJavaInstanceChildren(object))
        dict.update(get_dict_attr(object))
        dict.update(get_dir_attrs(object))
        dict = self.cleaned_dict(dict)
        return dict

    def cleaned_dict(self, dict):
        nicer_dict = {}
        for k, v in dict.items():
            try:
                # objects of type BuiltinFunctionType are resolved as java functions.
                if not type(v.value) is types.BuiltinFunctionType:
                    nicer_dict[k] = v
            except java.lang.InstantiationException: pass # against error browsing sys.modules
        return nicer_dict

    def getBaseClassesForObject(self, object, cls):
        dict = {}
        try:
            for b in cls.__bases__:
                key = argName(b)
                dict[key] = DObject(key, object, b)
        except: pass
        return dict

    def getInstanceChildren(self, dobject):
        dict = self.getFieldValues(dobject.value, dobject.cls)
        for k, v in dict.items():
            # avoid infinite recursion
            if v.cls is dobject.cls and v.value is dobject.value and v.icontype == dobject.icontype:
                del dict[k]
        dict.update(getConstructors(dobject.cls))
        dict.update(getMethods(dobject.cls))
        return self.cleaned_dict(dict)

    def getJavaInstanceChildren(self, java_instance):
        dict = {}
        try:
            cls = java.lang.Class.getClass(java_instance)
            key = argName(cls)
            dict[key] = DObject(key, java_instance, cls)
        except: pass
        return dict

    def getPyInstanceChildren(self, py_instance):
        dict = {}
        try:
            if type(py_instance) is types.InstanceType:
                for b in py_instance.__class__.__bases__:
                    if type(b) is types.TypeType:
                        key = b.__name__
                        dict[key] = DObject(key, py_instance, b)
        except: pass
        return dict

    def getFieldValues(self, object, cls, bases={}):
        dict = {}
        if type(cls) is not types.TypeType:
            return dict

        dict.update(self.getBaseClassesForObject(object, cls))
        fields = java.lang.Class.getDeclaredFields(cls)
        for f in fields:
            try:
                accessible = f.isAccessible()
                if accessible:
                    value = f.get(object)
                else:
                    f.setAccessible(1)
                    value = f.get(object)
                    f.setAccessible(accessible)
                key, value = self.get_DObjectForInstance(value, cls, f)
                if key:
                    dict[key] = value
            except (java.lang.IllegalAccessError,
                    java.lang.IllegalAccessException,
                    java.lang.IllegalArgumentException,
                    java.lang.NullPointerException), e:
                key, value = self.get_DObjectForInstance(object, cls, f, accessible=0)
                if key:
                    dict[key] = value
            except java.lang.Exception, e:
                import __main__
                print "Error: object=", object, " f:", f, "e:", e
                __main__.lasto = object
                __main__.lastf = f

        return dict

    def get_DObjectForInstance(self, object, cls, icontype, accessible=1):
        if is_primitive(object):
            key = docSignature(icontype)
            value = DObject(key, object, None, icontype, accessible)
        else:
            try:
                t = icontype.getType()
            except java.lang.IllegalAccessError:
                t = cls
            t_name = argName(t)
            key = docSignature(icontype) + " (" + t_name + ")"
            value = DObject(key, object, t, icontype, accessible)
        return key, value

    def getRefMethods(self, object):
        dict = {}
        try:
            for i in xrange(object.nargs):
                ref_args = object.argslist[i]
                m = ref_args.data
                key = docSignature(m)
                dict[key] = DObject(key, m)
        except: pass
        return dict

class ObjectTreeCellRenderer(swing.tree.DefaultTreeCellRenderer):

    def __init__(self, *args, **kwargs):
        swing.tree.DefaultTreeCellRenderer.__init__(self, *args, **kwargs)
        self.icon_path = os.path.join(os.path.dirname(__file__), "icons")
        self.icon_cache = {}

    def getTreeCellRendererComponent(self, tree, value, sel, expanded, leaf, row, hasFocus):
        swing.tree.DefaultTreeCellRenderer.getTreeCellRendererComponent(self, tree, value, sel, expanded, leaf, row, hasFocus)
        if isinstance(value, ObjectNode) and value.userObject is not None:
            self.setIcon(self.getMatchingIcon(value.userObject))
        return self

    def getIconnameForClass(self, dobject):
        otype = getObjectType(dobject)
        if otype:
            return objecttype2icon_dict[otype]
        else:
            return None

    def getMatchingIcon(self, dobject):
        icon_name = self.getIconnameForClass(dobject)
        if not self.icon_cache.has_key(icon_name):
            icon = swing.ImageIcon(os.path.join(self.icon_path, icon_name))
            self.icon_cache[icon_name] = icon
        return self.icon_cache[icon_name]

class ObjectTree:

    def __init__(self, info_display=None):
        self.data = {}
        self.info_display = info_display
        self.jtree = swing.JTree(valueChanged=self.valueChanged)
        self.jtree.getSelectionModel().setSelectionMode(swing.tree.TreeSelectionModel.SINGLE_TREE_SELECTION)
        tcr = ObjectTreeCellRenderer()
        self.jtree.setCellRenderer(tcr)
        self.current_node = None

    def update_changes(self, loc_del, loc_diff):
        if not loc_del and not loc_diff:
            return

        tps = self.get_expanded_paths()
        model = self.jtree.getModel()
        root = model.getRoot()
        # remove changed and deleted items
        loc_del.extend(loc_diff.keys())
        for d in loc_del:
            self.removeNode(root, d)
        # add changed and new items
        for k, v in loc_diff.items():
            self.insertNode(root, k, v)
        model.reload(root)
        self.set_expanded_paths(tps)

    def removeNode(self, tree, key):
        for i in xrange(tree.getChildCount()):
            if tree.getChildAt(i).userObject.key == key:
                tree.remove(i)
                break

    def insertNode(self, tree, key, value):
        o = DObject(key, value)
        node = ObjectNode(o)
        new_entry = [key, o]
        count = tree.getChildCount()
        idx = count
        for i in xrange(count):
            dobject = tree.getChildAt(i).userObject
            a = [dobject.key, dobject]
            if object_sort_cmp(a, new_entry) >= 0:
                idx = i
                break
        tree.insert(node, idx)

    def addNode(self, tree, key, value):
        node = ObjectNode(DObject(key, value))
        tree.add(node)

    def addLeaves(self, node, items):
        for key, value in items:
            self.addNode(node, key, value)

    def getComponent(self):
        return self.jtree

    def set_locals(self, locals):
        self.data = locals

    def init_model(self):
        tree = DefaultMutableTreeNode('Object Tree')
        items = self.data.items()
        items = map(lambda x: (x[0], DObject(x[0], x[1])), items)
        object_sort(items)
        items = map(lambda x: (x[0], x[1].value), items)
        self.addLeaves(tree, items)

        self.model = swing.tree.DefaultTreeModel(tree)
        self.jtree.setModel(self.model)

    def refresh(self):
        tps = self.get_expanded_paths()
        self.init_model()
        self.set_expanded_paths(tps)

    def get_expanded_paths(self):
        rc = self.jtree.getRowCount()
        tree_paths = []
        for i in xrange(rc):
            e = self.jtree.isExpanded(i)
            tp = self.jtree.getPathForRow(i)
            if e:
                tree_paths.append(tp)
        return tree_paths

    def set_expanded_paths(self, tree_paths):
        for p in tree_paths:
            self.expand_path(p)

    def expand_path(self, path):
        keys = []
        for n in path.getPath():
            keys.append(n.toString())
        rc = self.jtree.getRowCount()
        for i in xrange(rc):
            tp = self.jtree.getPathForRow(i)
            p = []
            for n in tp.getPath():
                p.append(n.toString())
            if keys == p:
                self.jtree.expandPath(tp)
                break

    def valueChanged(self, event):
        tree_path = event.getNewLeadSelectionPath()
        if tree_path:
            selected_node = tree_path.getLastPathComponent()
            # Ignore selection of root node.
            if isinstance(selected_node, ObjectNode):
                self.current_node = selected_node
                self.display_member()

    def display_member(self):
        if self.info_display:
            self.info_display.show_info(self.current_node)

class InfoDisplay(swing.JTextArea):

    def __init__(self, doc_browser=None, *args, **kwargs):
        swing.JTextArea.__init__(self, *args, **kwargs)
        self.doc_browser = doc_browser

    def show_info(self, node):
        dobject = node.userObject
        if not isinstance(dobject, DObject):
            return
        self.show_htmldoc(node)
        item, object, cls = dobject.key, dobject.value, dobject.cls

        text = ''
        text += str(item)
        text += '\n\nType: '
        if cls:
            text += argName(cls)
        elif type(object) == types.InstanceType:
            text += "instance of python class " + object.__class__.__name__
        elif type(object) == PyJavaInstance:
            jclass = java.lang.Class.getClass(object)
            jclass_name = java.lang.Class.getName(jclass)
            text += "instance of java class " + jclass_name
        else:
            text += str(type(object))
        if type(object) is types.StringType:
            value = repr(str(object))
        elif type(object) is PyStringMap:
            dict = {}
            dict.update(object)
            value = pprint.pformat(dict)
        else:
            value = str(object)

        if dobject.accessible:
            text += '\n\nValue: ' + value
        else:
            text += '\n\nValue not accessible'

        self.setText(text)
        self.setCaretPosition(0)

    def show_htmldoc(self, node):
        if not self.doc_browser:
            return

        dobject = node.userObject
        item = dobject.key
        object = dobject.icontype or dobject.cls or dobject.value

        if type(object) is types.TypeType:
            class_name = str(object)
            self.doc_browser.open_class_doc(class_name)
        elif is_documented_type(object):
            class_name = str(object.getDeclaringClass())
            sig = docSignature(object)
            self.doc_browser.open_class_doc(class_name, anchor=sig)
            ## print "showing:", sig, " with:", str(object)

class DocBrowser:
    def __init__(self, doc_url=None):
        self.editorPane = swing.JEditorPane(hyperlinkUpdate=self.hyperlinkUpdate)
        self.editorPane.setEditable(0)
        self.scrollPane = swing.JScrollPane(self.editorPane)
        self.doc_url = doc_url
        self.url = None

    def hyperlinkUpdate(self, event):
        if event.getEventType() == swing.event.HyperlinkEvent.EventType.ACTIVATED:
            url = event.getURL()
            self.open_url(url)

    def open_class_doc(self, class_name, anchor=None):
        if not self.doc_url:
            return
        url_path = class_name.replace('.', '/') + ".html"
        if type(self.doc_url) == type({}):
            # dictionary of paths
            urls = self.doc_url
            bestPrefix = ''
            for prefix in urls.keys():
                if class_name[:len(prefix)] == prefix:
                    if len(bestPrefix) < len(prefix):
                        bestPrefix = prefix
            if not bestPrefix:
                path = urls.get('default', None)
            else:
                path = urls[bestPrefix]
            if not path:
                return
            url_path = os.path.join(path, url_path)
        else:
            url_path = os.path.join(self.doc_url, url_path)
        if anchor:
            url_path += '#' + anchor
        # print "showing:", url_path
        self.open_url(url_path)

    def open_url(self, url):
        try:
            self.editorPane.setPage(url)
        except java.io.FileNotFoundException, e:
            # print "Unable to open url", url, "error:", e
            pass

    def get_pane(self):
        return self.scrollPane


class shell(swing.JPanel):
    currentpath="."

    def __init__(self, locals={}, doc_url={'default' : "file:///C:/java2/docs/api"}, size=(640, 480), exitOnClose=0, ownFrame=1, filename="untitled.py"):
        #~ def show_in_frame(self, ):
        self.locals=locals
        self.tempdir=tempfile.mkdtemp('_Py4IJSandbox_')
        sys.path.append(self.tempdir)
        
        def close_frame(event):
            w = event.getWindow()
            w.dispose()
        def exit_app(event):
            #sys.exit(0)
            pass
        if exitOnClose==1:
            close_func = exit_app
        else:
            close_func = close_frame
        self.test_console = JScrapbook.JScrapbook(locals=locals)
        self.console_pane = self.test_console

        self.doc_browser = DocBrowser(doc_url)
        self.doc_pane = self.doc_browser.get_pane()

        self.nodeinfo_pane = InfoDisplay(doc_browser=self.doc_browser)
        self.nodeinfo_pane.setPreferredSize(java.awt.Dimension(150, 150))

        self.o_tree = ObjectTree(info_display=self.nodeinfo_pane)
        self.o_tree.set_locals(locals)
        self.o_tree.init_model()
        self.test_console.diff_func = self.o_tree.update_changes

        self.tree = self.o_tree.getComponent()
        scroll_tree = swing.JScrollPane(self.tree)
        scroll_tree.setMinimumSize(java.awt.Dimension(180, 200))

        # define input, output, documentation, tree, value  and edit panes
        input=self.console_pane.input
        output=self.console_pane.output
        doc=self.doc_pane
        tree=scroll_tree
        value=swing.JScrollPane(self.nodeinfo_pane)

        # define main frame{'default' : "file:///C:/java2/docs/api"}
        self.frame = swing.JFrame("Py4IJ", windowClosing=close_func)
        self.frame.contentPane.add(self)
        cp=self.frame.getContentPane()
        cp.setLayout(java.awt.BorderLayout())

        # define console
        self.jconsole = console.Console(self.frame, namespace=self.locals)

        # define tabbed pane
        self.tabPane=swing.JTabbedPane()
        self.tabPane.addTab("Console",swing.JScrollPane(self.jconsole.text_pane))
        self.tabPane.addTab("Editor",input)
        self.editor=input.text_pane
        font_family = "Monospaced" # jEdit.getProperty("jython.font", "Monospaced")
        font_size = 14 #jEdit.getIntegerProperty("jython.fontsize", 14)
        font_style = Font.PLAIN #jEdit.getIntegerProperty("jython.fontstyle", Font.PLAIN)
        self.editor.setFont(Font(font_family,font_style,font_size))
        self.filename=filename
        if filename!="untitled.py":
            self.frame.title=self.filename
            text=open(self.filename).read()
            self.editor.text=text
        self.tabPane.addTab("Doc",doc)
        # tabPane.addTab("Value",value)
        # tabPane.addTab("Editor",swing.JScrollPane(editor))
        # rightPane=swing.JSplitPane(swing.JSplitPane.VERTICAL_SPLIT,self.tabPane,output)
        # cp.add(swing.JSplitPane(swing.JSplitPane.HORIZONTAL_SPLIT,tree,rightPane))
        cp.add(swing.JSplitPane(swing.JSplitPane.HORIZONTAL_SPLIT,tree,self.tabPane))

        self.frame.pack()

        # define menu bar
        mb=swing.JMenuBar()
        m1,m2=map(swing.JMenu,["File","Edit"])
        map(mb.add,[m1,m2])
        self.frame.setJMenuBar(mb)
        mFile,mEdit=m1,m2
        miNew, miOpen, miSave, miSaveAs, miExit, miRunCodeConsole, \
        miPasteAsString, miPasteAsFloat, miPasteAsInteger, miRefresh\
        = map(swing.JMenuItem,\
        ["New", "Open", "Save", "Save as...", "Exit","Run Code in Console", \
        "Paste as String", "Paste as Float", "Paste as Integer","Refresh"])
        mItems=\
        [miNew, miOpen, miSave, miSaveAs, miExit, \
        miPasteAsString, miPasteAsFloat, miPasteAsInteger,miRefresh]
        map(mFile.add,[miNew, miOpen, miSave, miSaveAs, miRunCodeConsole, miExit])
        map(mEdit.add,[miPasteAsString, miPasteAsFloat, miPasteAsInteger,miRefresh])

        # define menu actions
        self.consolePane=self.console_pane
        self.consoleFrame=self.frame

        miNew.actionPerformed=self.actionNew
        miOpen.actionPerformed=self.actionOpen
        miOpen.setAccelerator(getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK))
        miSave.actionPerformed=self.actionSave
        miSave.setAccelerator(getKeyStroke(KeyEvent.VK_Z, KeyEvent.CTRL_MASK))
        miSaveAs.actionPerformed=self.actionSaveAs
        miSaveAs.setAccelerator(getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK))
        miExit.actionPerformed=lambda e:sys.exit()
        miPasteAsString.actionPerformed=self.actionPasteAsString
        miPasteAsFloat.actionPerformed=self.actionPasteAsFloat
        miPasteAsInteger.actionPerformed=self.actionPasteAsInteger
        miRunCodeConsole.actionPerformed=self.actionRunCodeConsole
        miRunCodeConsole.setAccelerator(getKeyStroke(KeyEvent.VK_R, KeyEvent.CTRL_MASK))
        miRefresh.actionPerformed=self.actionRefresh
        #~ refresh_button = javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, 0)
        #~ refresh_button_action = RefreshAction()
        #~ inputMap = self.getInputMap(javax.swing.JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
        #~ actionMap = self.getActionMap()
        #~ inputMap.put(refresh_button, "refresh_button_pressed")
        #~ actionMap.put("refresh_button_pressed", refresh_button_action)
        #~ refresh_button_action.setRefreshFunc(self.o_tree.refresh)

        self.frame_size=size
        self.frame.setSize(apply(java.awt.Dimension, size))
        self.frame.setVisible(1)

    def actionNew(self, e):
        self.filename="untitled.py"
        self.editor.text=""
        self.frame.title=self.filename
        self.tabPane.setSelectedIndex(1) 
    def actionSaveAs(self,e):
        fileChooser=swing.JFileChooser(self.currentpath)
        fileChooser.showSaveDialog(self.consoleFrame)
        self.filename=os.path.join(fileChooser.getCurrentDirectory().getAbsolutePath(),fileChooser.getSelectedFile().getName())
        text=self.editor.getText()
        f=open(self.filename,"w")
        f.write(text)
        f.close()
        self.frame.title=self.filename
    def actionSave(self,e):
        text=self.editor.getText()
        f=open(self.filename,"w")
        f.write(text)
        f.close()
    def actionOpen(self,e):
        fileChooser=swing.JFileChooser(self.currentpath)
        fileChooser.showOpenDialog(self.consoleFrame)
        self.currentpath=fileChooser.getCurrentDirectory().getAbsolutePath()
        self.filename=os.path.join(self.currentpath,fileChooser.getSelectedFile().getName())
        # print self.filename
        text=open(self.filename).read()
        # print text
        self.editor.text=text
        self.frame.title=self.filename
        self.tabPane.setSelectedIndex(1) 
    def actionCut(self,e):
        pass
    def actionCopy(self,e):
        pass
    def actionPaste(self,e):
        pass
    def actionPasteAsString(self,e):
        toolkit = Toolkit.getDefaultToolkit()
        clipboard = toolkit.getSystemClipboard()
        clipString=clipboard.getContents(None).getTransferData(DataFlavor.stringFlavor)
        self.consolePane.locals["clipString"]=clipString
    def actionPasteAsFloat(self,e):
        toolkit = Toolkit.getDefaultToolkit()
        clipboard = toolkit.getSystemClipboard()
        clipString=clipboard.getContents(None).getTransferData(DataFlavor.stringFlavor)
        self.consolePane.locals["clipFloat"]=map(float,clipString.split())
    def actionPasteAsInteger(self,e):
        toolkit = Toolkit.getDefaultToolkit()
        clipboard = toolkit.getSystemClipboard()
        clipString=clipboard.getContents(None).getTransferData(DataFlavor.stringFlavor)
        self.consolePane.locals["clipInteger"]=map(int,clipString.split())
    def actionRunCodeConsole(self, e=None, filepath=None, local_namespace={}, global_namespace={}):
        self.tabPane.setSelectedIndex(0)
        # Add file folder to sys.path
        if filepath==None: path=os.path.split(self.filename)[0]
        else: path=os.path.split(filepath)[0]
        added_path=False
        if (filepath!=None or os.path.exists(self.filename)) and not path in sys.path:
            sys.path.append(path)
            added_path=True
        # Run code
        try:
          if local_namespace=={}: local_namespace=self.locals
          if filepath!=None:
               #name=os.path.splitext(os.path.split(filepath)[1])[0]
               #imp.load_source(name, filepath)
               execfile(filepath, global_namespace, local_namespace)
          else:
               tempfile=os.path.split(self.filename)[1]
               tempfile=os.path.join(self.tempdir, tempfile)
               codetxt=self.editor.getText().replace('?','')
               f=open(tempfile, 'w')
               f.write(codetxt)
               f.close()
               #name=os.path.splitext(os.path.split(tempfile)[1])[0]
               #imp.load_source(name, filepath)
               execfile(tempfile, global_namespace, local_namespace)
          if added_path: sys.path.pop(-1)
        except Exception, detail:
          if filepath: self.jconsole.printError('Error in %s' % filepath)
          else: self.jconsole.printError('Error in Editor text')
          self.jconsole.printError('Error type: %s' % repr(type(detail)))
          #traceback.print_last()
          if 'message' in dir(detail): self.jconsole.printError(detail.message)
          elif 'value' in dir(detail): self.jconsole.printError(detail.value)
          ##if 'args' in dir(detail): self.jconsole.printError(repr(detail.args))
          if 'filename' in dir(detail): self.jconsole.printError("Error in %s" % detail.filename)
          if 'msg' in dir(detail): self.jconsole.printError("Problem: "+detail.msg)
          if 'text' in dir(detail): self.jconsole.printError("In code:"+detail.text)
          self.locals['error']=detail
          #print "~~~~~~~"
          #traceback.print_last()
          self.jconsole.resetbuffer()
          self.jconsole.printPrompt()
          self.actionRefresh()
          return detail
          #if filepath==None: self.jconsole.printError("Error in %s" % self.filename)
          #else: self.jconsole.printError("Error in %s" % filepath)
          #self.jconsole.printError("%s: %s" % (str(type(detail)).split("'")[1], detail.message))
          #self.jconsole.printError("2"+str(detail.text))
          #self.jconsole.printError("3"+str(detail.message))
          #self.jconsole.printError("4"+str(detail.offset))
          #self.jconsole.printError("5"+str(detail.lineno))
          #self.jconsole.printError("6"+str(detail.msg))
          #self.jconsole.printError("7"+str(detail.filename))
          #self.jconsole.printError("8"+str(detail.args))
        self.jconsole.resetbuffer()
        self.jconsole.printPrompt()
        self.actionRefresh()
        return True
    def actionRefresh(self,e=None):
        self.o_tree.refresh()

class RefreshAction(javax.swing.AbstractAction):
    def actionPerformed(self, event):
        self.refresh_func()

    def setRefreshFunc(self, refresh_func):
        self.refresh_func = refresh_func

if __name__=="__main__":
    import jcrust
    jcrust.shell(locals=locals())

